package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private VodService vodService;
    @Autowired
    private TrackStatMapper trackStatMapper;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;



    /**
     * 保存音频信息
     *
     * @param trackInfoVo 音轨信息的Vo对象，包含了需要保存的音轨的基本信息
     * @param userId      创建音轨的用户ID
     *                    <p>
     *                    此方法负责将TrackInfoVo对象中的数据转换为TrackInfo对象，并设置用户ID和订单编号等信息后，插入数据库
     *                    同时，此方法还会保存音轨的统计信息，并更新相应专辑的音轨数量
     */
	/*
	@Transactional(rollbackFor = Exception.class)
	@Override
	public void saveTrackInfo(TrackInfoVo trackInfoVo,Long userId) {
	    // 创建一个新的TrackInfo对象
	    TrackInfo trackInfo = new TrackInfo();
	    // 将TrackInfoVo对象的属性复制到TrackInfo对象中
	    BeanUtils.copyProperties(trackInfoVo,trackInfo);
	    // 设置音轨的用户ID
	    trackInfo.setUserId(userId);
	    // 获取并设置音轨的订单编号
	    trackInfo.setOrderNum(albumInfoMapper.selectById(trackInfo.getAlbumId()).getIncludeTrackCount()+1);
	    // 获取音轨的媒体信息
	    TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfoVo(trackInfo.getMediaFileId());

	    // 设置音轨的媒体大小
	    trackInfo.setMediaSize(trackMediaInfoVo.getSize());
	    // 设置音轨的媒体时长
	    trackInfo.setMediaDuration(trackMediaInfoVo.getDuration());
	    // 设置音轨的媒体URL
	    trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
	    // 设置音轨的媒体类型
	    trackInfo.setMediaType(trackMediaInfoVo.getType());
	    // 插入音轨信息到数据库
	    trackInfoMapper.insert(trackInfo);

	    // 保存音轨的播放统计信息
	    this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
	    // 保存音轨的收藏统计信息
	    this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
	    // 保存音轨的点赞统计信息
	    this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
	    // 保存音轨的评论统计信息
	    this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);

	    // 获取音轨所属的专辑信息
	    AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
	    // 更新专辑的音轨数量
	    albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount()+1);
	    // 更新专辑信息到数据库
	    albumInfoMapper.updateById(albumInfo);
	}*/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
        TrackInfo trackInfo = new TrackInfo();
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        trackInfo.setUserId(userId);
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfoVo(trackInfo.getMediaFileId());
        trackInfo.setMediaDuration(trackMediaInfoVo.getDuration());
        trackInfo.setMediaType(trackMediaInfoVo.getType());
        trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
        trackInfoMapper.insert(trackInfo);
        //track_stat
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);
        //album_info
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);

    }

    private void saveTrackStat(Long id, String statPlay) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(id);
        trackStat.setStatType(statPlay);
        trackStat.setStatNum(new Random().nextInt(100000));
        trackStatMapper.insert(trackStat);
    }

    /**
     * 分页查询声音列表
     *
     * @param listVoPage
     * @param trackInfoQuery
     * @return
     */
    @Override
    public IPage<TrackListVo> findUserTrackPage(Page<TrackListVo> listVoPage, TrackInfoQuery trackInfoQuery) {
        return trackInfoMapper.selectUserTrackPage(listVoPage, trackInfoQuery);
    }

    /**
     * 删除声音信息
     *
     * @param trackId 声音轨道的ID，用于定位并删除特定的声音信息
     *                <p>
     *                该方法负责删除系统中的声音信息，包括从数据库中删除音频信息、更新相关专辑的信息、
     *                删除音频的统计信息以及删除与音频关联的媒体文件
     */
	/*
	@Transactional(rollbackFor = Exception.class)
	@Override
	public void removeTrackInfo(Long trackId) {
	    // 根据音频ID查询轨道信息，以便后续操作
	    TrackInfo trackInfo = trackInfoMapper.selectById(trackId);

	    // 删除音频信息
	    trackInfoMapper.deleteById(trackId);

	    // 更新音频的排序编号，保持数据一致性
	    trackInfoMapper.updateOrderNum(trackInfo.getId(),trackInfo.getOrderNum());

	    // 根据专辑ID查询专辑信息，准备更新专辑包含的音频数量
	    AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());

	    // 减少专辑包含音频的数量，因为一个轨道已被删除
	    albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount()-1);

	    // 更新专辑信息
	    albumInfoMapper.updateById(albumInfo);

	    // 删除与音频相关的统计信息
	    trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId,trackId));

	    // 删除与音频关联的媒体文件
	    vodService.deleteMedia(trackInfo.getMediaFileId());
	}
	*/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeTrackInfo(Long trackId) {
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        //track_info
        trackInfoMapper.deleteById(trackId);
        trackInfoMapper.updateOrderNum(trackInfo.getAlbumId(), trackInfo.getOrderNum());
        //track_stat
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, trackId));
        //album_info
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);
        //vod
        vodService.deleteMedia(trackInfo.getMediaFileId());
    }

    /**
     * 更新音轨信息
     *
     * @param trackId 音轨ID，用于标识特定的音轨
     * @param trackInfoVo 包含更新信息的音轨对象，用于更新音轨信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateTrackInfo(Long trackId, TrackInfoVo trackInfoVo) {
        // 根据音轨ID查询数据库中的音轨信息
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        // 获取原始的媒体文件ID
        String mediaFileId = trackInfo.getMediaFileId();
        // 将传入的音轨信息对象的属性复制到数据库中的音轨信息对象
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        // 检查媒体文件ID是否被修改
        if (!mediaFileId.equals(trackInfoVo.getMediaFileId())){
            // 根据原始媒体文件ID获取媒体信息
            TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfoVo(mediaFileId);
            // 如果媒体信息为空，则抛出异常，指示无效的媒体文件ID
            if (trackMediaInfoVo == null){
                throw new GuiguException(ResultCodeEnum.VOD_FILE_ID_ERROR);
            }
            // 更新音轨信息中的媒体URL、大小、类型和时长
            trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            trackInfo.setMediaDuration(trackMediaInfoVo.getDuration());
            // 删除原始媒体文件
            vodService.deleteMedia(mediaFileId);
        }
        // 更新数据库中的音轨信息
        trackInfoMapper.updateById(trackInfo);
    }

    @Override
    public TrackInfo getTrackInfo(Long trackId) {
        return trackInfoMapper.selectById(trackId);
    }

    /**
     * 批量获取用户下单声音列表
     * @param trackId 音轨ID
     * @param trackCount 需要获取的音轨数量
     * @param userId 用户ID
     * @return 返回用户下单的音轨列表
     */
    @Override
    public List<TrackInfo> fingPaidTrackInfoList(Long trackId, Integer trackCount, Long userId) {
        // 根据音轨ID获取音轨信息
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        // 调用远程服务获取用户付费的音轨列表
        Result<List<Long>> userPaidTrackListResult = userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
        // 确保远程调用成功
        Assert.notNull(userPaidTrackListResult,"远程调用失败");
        List<Long> userPaidTrackList = userPaidTrackListResult.getData();

        // 初始化音轨信息列表
        List<TrackInfo> trackInfoList = new ArrayList<>();
        // 如果请求的音轨数量大于0，则进行查询
        //select * from track_info where id >= 5633 and album_id = 110 and id  not in(5635) limit 8
        if (trackCount>0){
            // 构建查询条件
            LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TrackInfo::getAlbumId,trackInfo.getAlbumId()).gt(TrackInfo::getOrderNum,trackInfo.getOrderNum());
            // 如果用户付费音轨列表不为空，则添加不在列表中的条件
            if (!CollectionUtils.isEmpty(userPaidTrackList)){
                queryWrapper.notIn(TrackInfo::getId,userPaidTrackList);
            }
            // 添加查询数量限制
            queryWrapper.last("limit "+trackCount);
            // 执行查询
            trackInfoList = trackInfoMapper.selectList(queryWrapper);
        }else {
            // 如果请求的音轨数量不大于0，直接返回当前音轨信息
            trackInfoList.add(trackInfo);
        }

        // 返回查询结果
        return trackInfoList;
    }

    /**
     * 获取用户声音分集购买支付列表
     * 根据用户和声音ID，获取该用户未购买的声音轨列表及其价格信息
     * @param trackId 音轨ID
     * @param userId 用户ID
     * @return 未购买声音的列表，包括声音名称、价格和声音数量
     */
    @Override
    public List<Map<String, Object>> findUserTrackPaidList(Long trackId, Long userId) {
        // 根据声音ID获取音轨信息
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        // 根据声音所属专辑ID获取专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        // 调用远程服务获取用户已购买的声音ID列表
        Result<List<Long>> trackIdListResult  =  userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
        // 确保远程调用成功
        Assert.notNull(trackIdListResult,"远程调用失败");
        // 获取用户已购买的声音ID列表
        List<Long> trackPaidIdList = trackIdListResult.getData();

        // 查询声音所属专辑中，顺序号大于当前声音的声音ID列表
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(
                new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getAlbumId, trackInfo.getAlbumId())
                        .gt(TrackInfo::getOrderNum, trackInfo.getOrderNum()).select(TrackInfo::getId));

        // 将查询结果转换为声音ID列表
        List<Long> trackIdAllList = trackInfoList.stream().map(TrackInfo::getId).collect(Collectors.toList());
        // 初始化未重复的声音ID列表
        List<Long> trackIdNoReaptList = new ArrayList<>();

        // 根据用户已购买的声音ID列表，确定未购买的声音ID列表
        if (CollectionUtils.isEmpty(trackPaidIdList)){
            trackIdNoReaptList = trackIdAllList;
        }else {
            trackIdNoReaptList = trackIdAllList.stream().filter(itemId ->!trackPaidIdList.contains(itemId)).collect(Collectors.toList());
        }

        // 初始化返回的列表
        List<Map<String, Object>> list = new ArrayList<>();
        // 添加当前声音的价格信息
        if (trackIdNoReaptList.size()>=0){
            Map<String, Object> map = new HashMap<>();
            map.put("name", "本集");
            map.put("price", albumInfo.getPrice());
            map.put("trackCount", 0);
            list.add(map);
        }
        // 根据未购买的声音数量，添加不同的价格选项
        if (trackIdNoReaptList.size()>0 && trackIdNoReaptList.size() <= 10){
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));
            map.put("name","后"+trackIdNoReaptList.size()+"集");
            map.put("price",price);
            map.put("trackCount",count);
            list.add(map);
        }
        if (trackIdNoReaptList.size()>10){
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(10));
            map.put("name","后十集");
            map.put("price",price);
            map.put("trackCount",10);
            list.add(map);
        }

        if(trackIdNoReaptList.size() > 10 && trackIdNoReaptList.size() <= 20) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));
            map.put("name", "后"+count+"集");
            map.put("price", price);
            map.put("trackCount", count);
            list.add(map);
        }
        if(trackIdNoReaptList.size() > 20) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(20));
            map.put("name", "后20集");
            map.put("price", price);
            map.put("trackCount", 20);
            list.add(map);
        }

        if(trackIdNoReaptList.size() > 20 && trackIdNoReaptList.size() <= 30) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));
            map.put("name", "后"+count+"集");
            map.put("price", price);
            map.put("trackCount", count);
            list.add(map);
        }
        if(trackIdNoReaptList.size() > 30) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(30));
            map.put("name", "后30集");
            map.put("price", price);
            map.put("trackCount", 30);
            list.add(map);
        }

        // 返回未购买声音的价格选项列表
        return list;
    }

    /**
     * 更新指定专辑和音轨的统计信息
     * 此方法使用事务处理，确保在更新统计信息时的数据一致性和完整性
     *
     * @param albumId 专辑的唯一标识符
     * @param trackId 音轨的唯一标识符
     * @param statType 统计类型，例如播放次数、点赞次数等
     * @param count 要增加的统计数值
     */
    @Transactional
    @Override
    public void updateStat(Long albumId, Long trackId, String statType, Integer count) {
        // 更新音轨的统计信息
        trackInfoMapper.updateStat(trackId,statType,count);

        // 当统计类型为播放次数时，同时更新专辑的播放统计信息
        if (statType.equals(SystemConstant.TRACK_STAT_PLAY)) {
            albumInfoMapper.updateStat(albumId,SystemConstant.TRACK_STAT_PLAY,count);
        }
    }

    @Override
    public IPage<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> pageParam, Long albumId, Long userId) {
        IPage<AlbumTrackListVo> pageInfo = trackInfoMapper.selectAlbumInfoPage(pageParam,albumId);
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (null == userId){
            if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())){
                /*for (AlbumTrackListVo albumTrackListVo : pageInfo.getRecords()) {
                    if ( albumTrackListVo.getOrderNum()>albumInfo.getTracksForFree()) {
                       albumTrackListVo.setIsShowPaidMark(true);
                    }
                }*/
                pageInfo.getRecords().stream().filter(albumTrackListVo->albumTrackListVo.getOrderNum()>albumInfo.getTracksForFree()).forEach(albumTrackListVo -> albumTrackListVo.setIsShowPaidMark(true));
            }
            return pageInfo;
        }
        if (null != userId){
            boolean isNeedPay = false;
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())){
                //vip免费
                //判断用户身份
                Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfo(userId);
                Assert.notNull(userInfoVoResult,"远程调用失败");
                UserInfoVo userInfoVo = userInfoVoResult.getData();
                Assert.notNull(userInfoVo,"远程调用失败");
                if (0 == userInfoVo.getIsVip() || (1==userInfoVo.getIsVip()&&userInfoVo.getVipExpireTime().before(new Date()))){
                    isNeedPay=true;
                }
            } else if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(albumInfo.getPayType())) {
                //付费
                isNeedPay = true;
            }
            if (isNeedPay){
                List<AlbumTrackListVo> albumTrackListVo = pageInfo.getRecords().stream().filter(albumTrackVo -> albumTrackVo.getOrderNum() > albumInfo.getTracksForFree()).collect(Collectors.toList());
                List<Long> trackNeedPayList = albumTrackListVo.stream().map(AlbumTrackListVo::getTrackId).collect(Collectors.toList());
                Result<Map<Long, Integer>> mapResult = userInfoFeignClient.userIsPaidTrack(albumId,trackNeedPayList);
                Assert.notNull(mapResult,"远程调用失败");
                Map<Long, Integer> map = mapResult.getData();
                Assert.notNull(map,"远程调用失败");
                for (AlbumTrackListVo trackListVo : albumTrackListVo) {
                    trackListVo.setIsShowPaidMark(map.get(trackListVo.getTrackId())==0);
                }
            }
        }
        return pageInfo;
    }
}
